/*
 * @lc app=leetcode.cn id=65 lang=cpp
 *
 * [65] 有效数字
 */
#include "include.h"
// @lc code=start
/**
 * state machine 反而又慢又占ram ？？？
*/
class Solution {
public:
    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_WITHOUT_INT,
        STATE_FRACTION,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUMBER,
        STATE_END
    };

    enum CharType {
        CHAR_NUMBER,
        CHAR_EXP,
        CHAR_POINT,
        CHAR_SIGN,
        CHAR_ILLEGAL
    };

    CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CHAR_EXP;
        } else if (ch == '.') {
            return CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CHAR_SIGN;
        } else {
            return CHAR_ILLEGAL;
        }
    }

    bool isNumber(string s) {
        unordered_map<State, unordered_map<CharType, State>> transfer{
            {
                STATE_INITIAL, {
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_POINT, STATE_POINT_WITHOUT_INT},
                    {CHAR_SIGN, STATE_INT_SIGN}
                }
            }, {
                STATE_INT_SIGN, {
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_POINT, STATE_POINT_WITHOUT_INT}
                }
            }, {
                STATE_INTEGER, {
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_EXP, STATE_EXP},
                    {CHAR_POINT, STATE_POINT}
                }
            }, {
                STATE_POINT, {
                    {CHAR_NUMBER, STATE_FRACTION},
                    {CHAR_EXP, STATE_EXP}
                }
            }, {
                STATE_POINT_WITHOUT_INT, {
                    {CHAR_NUMBER, STATE_FRACTION}
                }
            }, {
                STATE_FRACTION,
                {
                    {CHAR_NUMBER, STATE_FRACTION},
                    {CHAR_EXP, STATE_EXP}
                }
            }, {
                STATE_EXP,
                {
                    {CHAR_NUMBER, STATE_EXP_NUMBER},
                    {CHAR_SIGN, STATE_EXP_SIGN}
                }
            }, {
                STATE_EXP_SIGN, {
                    {CHAR_NUMBER, STATE_EXP_NUMBER}
                }
            }, {
                STATE_EXP_NUMBER, {
                    {CHAR_NUMBER, STATE_EXP_NUMBER}
                }
            }
        };

        int len = s.length();
        State st = STATE_INITIAL;

        for (int i = 0; i < len; i++) {
            CharType typ = toCharType(s[i]);
            if (transfer[st].find(typ) == transfer[st].end()) {
                return false;
            } else {
                st = transfer[st][typ];
            }
        }
        return st == STATE_INTEGER || st == STATE_POINT || st == STATE_FRACTION || st == STATE_EXP_NUMBER || st == STATE_END;
    }
};
// @lc code=end

// ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"]

// ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]

int main(){
    string s = "+.8";
    Solution test;
    bool res =test.isNumber(s);
    printf("res %d\n", res);
}

class SolutionDivideAndConquer {
public:
    bool isNumber(string s) {
        int end = s.size()-1;

        auto find_e = s.find_first_of('e');
        auto find_E = s.find_first_of('E');

        if (find_e != -1 and find_E != -1){
            return false;
        }else if (find_e != -1 and find_E == -1){
            // e
            return isValidFloat(s, 0, find_e-1) and isValidInt(s, find_e+1, end);
        }else if (find_e == -1 and find_E != -1){
            // E
            return isValidFloat(s, 0, find_E-1) and isValidInt(s, find_E+1, end);
        }else{
            // no e/E
            return isValidFloat(s, 0, end);
        }

        return true;
    }
private:
    bool isValidInt(const string& s, int start, int end){
        int find_plus = s.find_first_of('+', start);
        int find_minus = s.find_first_of('-', start);

        bool no_plus = find_plus == -1 or find_plus > end;
        bool no_minus = find_minus == -1 or find_minus > end;

        if (!no_plus and !no_minus){
            return false;
        }else if (no_plus and !no_minus){
            if (find_minus != start){return false;}
            return isNumArray(s, start+1, end);
        }else if (!no_plus and no_minus){
            if (find_plus != start){return false;}
            return isNumArray(s, start+1, end);
        }else{
            return isNumArray(s, start, end);
        }
        return true;
    }
    bool isValidFloat(const string& s, int start, int end){
        int find_dot = s.find_first_of('.', start);

        bool no_dot = find_dot == -1 or find_dot > end;

        if (!no_dot){
            if (start == find_dot){
                return isNumArray(s, find_dot+1, end);
            }else if (find_dot == end){
                return isValidInt(s, start, find_dot-1);
            }else{
                if (start == find_dot-1 and (s[start] == '+' or s[start] == '-')){
                    return isNumArray(s, find_dot+1, end);
                }
                return isValidInt(s, start, find_dot-1) and isNumArray(s, find_dot+1, end);
            }
        }

        return isValidInt(s, start, end);
    }
private:
    inline bool isNumArray(const string& s, int start, int end){
        // at least one num
        if (start > end){return false;}

        for (int i=start;i<=end;++i){
            if (!isNum(s[i])){
                return false;
            }
        }
        return true;
    }
    inline bool isNum(char c){
        return c-'0' >= 0 and c-'0' <= 9;
    }
};
